#!/usr/bin/env python

# file:       trailersplaylist.py
# project:    http://code.google.com/p/appleplaylist/
# author:     Brett Sanche (thinkofwhy)
# license:    Creative Commons GNU GPL v2 (http://creativecommons.org/licenses/GPL/2.0/)

# Thanks to dbr @ github for adding some unicode management code to this file :)
# This module uses dbr's appletrailers.py module to acquire trailer info from Apple.com
# It's been developed on a Vista x64 platform for Windows Media Player 11 using ffdshow
# It's designed to create an asx playlist of Apple.com movie posters and trailers
# It displays a movie's poster for a specified duration (10 seconds) and then its trailer
# The m3u playlist excludes the posters.

"""
  options:
  -h       help
  -q val   Video quality (val = 480 | 720)
  -p val   Posted date offset (val = number of days before today)
  -s val   Days since release (val = number of days before today)
  -r val   Days to release (val = number of days after today)
  -d val   directory path to write data (exclude filename)
  -w val   write trailer info to file ( val = 0:normal | 1:minimal)
  -u       use trailer poster
  -i       show trailer info on screen
  
  long format options: --help, --quality=, --postdate=, "days_since_release=", "days_to_release=" , --dirpath=, --use_poster, --write_info, --show_info

"""

import sys, os, time, getopt
import datetime
from datetime import date
from appletrailers import Trailers

fn_m3u = "appletrailers.m3u"
fn_ASX = "appletrailers.asx"
fn_upd = "appletrailers.upd"
fn_txt = "appletrailers.txt"
DEF_QUALITY         = "default"
DEF_POSTER_DURATION = "00:10"

class Movies:
	def __init__(self, postdate_offset = None, days_since_releasedate_offset = None, days_to_releasedate_offset = None, quality = None, poster_flag = True, path = None):
		self.postdate_offset            = postdate_offset
		self.neg_releasedate_offset     = days_since_releasedate_offset
		self.pos_releasedate_offset     = days_to_releasedate_offset
		self.quality                    = quality
		self.display_poster             = poster_flag
		self.path                       = path
		self.movies                     = None
		self.last_update                = None
				

		self.update_path = os.path.join(self.path, fn_upd)

		if not postdate_offset and not days_since_releasedate_offset and not days_to_releasedate_offset and os.path.exists(self.update_path):
			self.Read_LastUpdate()

	def HandleUnicode(self, str):
		## I know, I know. But this is just a temporary hack.
		return str.replace(u'\u2019',"'").__str__().encode("UTF-8","ignore")

	def RunTimeM3uString(self, ts):
		st = ts.split(':')
		if len(st[0]) > 0:
			return str(int(st[0]) * 60 + int(st[1]))
		else:
			return str(int(st[1]))

	def Write_ASX(self):
		path = os.path.join(self.path, fn_ASX)
		
		print "Writing asx file to: ", path

		try:
			f = open(path,'w+')

			f.write("<ASX version = \"3.0\">\n")
			f.write("<Title>Apple Trailers</Title>\n")

			for m in self.movies:
				if self.display_poster:
					poster = m.poster.xlarge if m.poster.xlarge else m.poster.location

					f.write("<Entry>\n")
					f.write("  <Title>" + self.HandleUnicode(m.info.title)  + "</Title>\n")
					f.write("  <Duration value=\"" + DEF_POSTER_DURATION + "\" />\n")
					f.write("  <Ref href = \"" + self.HandleUnicode(poster) + "\" />\n")
					f.write("</Entry>\n")

				f.write("<Entry>\n")
				f.write("  <Title>" + self.HandleUnicode(m.info.title) + "</Title>\n")
				f.write("  <Duration value=\"" + m.info.runtime + "\" />\n")
				f.write("  <Ref href = \"" + self.HandleUnicode(m.preview.large) + "\" />\n")
				f.write("</Entry>\n")

			f.write("</ASX>\n")
			f.close()
		except IOError:
			print "ERROR - Movies()::Write_ASX(): Failed."
			raise
		
	def Write_m3u(self):
		path = os.path.join(self.path, fn_m3u)
		print "Writing m3u file to: ", path

		try:
			f = open(path,'w+')
			f.write("#EXTM3U\n")
			for m in self.movies:
				rts = self.RunTimeM3uString(m.info.runtime)
				f.write("#EXTINF: %s,%s\n" % (rts, self.HandleUnicode(m.info.title)))
				f.write(self.HandleUnicode(m.preview.large) + '\n')
			f.close()
		except IOError:
			print "ERROR - Movies()::Write_m3u(): Failed."
			raise

	def Write_TrailerInfo(self, minimal = False):
		path = os.path.join(self.path, fn_txt)
		print "Writing trailer info to: ", path

		try:
			f = open(path,'w+')
			if minimal:
                                for m in self.movies:
                                        f.write( self.HandleUnicode(m.poster.location) + '\n')
                                        f.write( self.HandleUnicode(m.preview.large) + '\n')
                        else:
                                for m in self.movies:
                                        f.write( "Title: " + self.HandleUnicode(m.info.title) + '\n')
                                        f.write( "Run Time: " + m.info.runtime + '\n')
                                        f.write( "Rating: " + m.info.rating + '\n')
                                        f.write( "Release Date: " + m.info.releasedate + '\n')
                                        f.write( "Post Date: " + m.info.postdate + '\n')
                                        f.write( "Poster: " + self.HandleUnicode(m.poster.location) + '\n')
                                        f.write( "Actors: " + ", ".join(x for x in m.cast).encode("UTF-8","ignore") + '\n')
                                        f.write( "Trailer: " + self.HandleUnicode(m.preview.large) + '\n')
                                        f.write( "*"*24 + '\n')

			f.close()
		except IOError:
			print "ERROR - Movies()::Write_TrailerInfo(): Failed."
			raise

	def ShowInfo(self):
		print "Apple trailer info:\n\n"
		for trailer in self.movies:
			print "Title: ", self.HandleUnicode(trailer.info.title)
			print "Run Time: ", trailer.info.runtime
			print "Rating: ", trailer.info.rating
			print "Release Date: ", trailer.info.releasedate
			print "Post Date: ", trailer.info.postdate
			print "Poster: ", self.HandleUnicode(trailer.poster.location)
			print "Actors: ", ", ".join(x for x in trailer.cast).encode("UTF-8","ignore")
			print "Trailer: ", self.HandleUnicode(trailer.preview.large)
			print "*"*24

	def Write_LastUpdate(self):
		try:
			f = open(self.update_path,'w')
			f.write(date.isoformat(date.today()))
			f.close()
		except IOError:
			print "ERROR - Movies()::Write_LastUpdate(): Failed."
			raise

	def Read_LastUpdate(self):
		try:
			f = open(self.update_path)
			self.last_update = f.read(10)
			f.close()
		except IOError:
			print "ERROR - Movies()::Read_LastUpdate(): Failed."
			raise

	def FixDate(self, s):
                if s != None:
        		return datetime.datetime(int(s[:4]), int(s[5:7]), int(s[8:10]))
        	else:
                        return None

	def OffsetDateFromToday(self, today, offset):
                return today + datetime.timedelta(offset)

	def GetMovies(self):
		today = datetime.datetime.today()

		if self.quality == None:
			movies = Trailers()
		else:
			movies = Trailers(self.quality)

		from_postdate = None
		to_releasedate = None
		from_releasedate = None
		
		if not self.postdate_offset and not self.pos_releasedate_offset and not self.neg_releasedate_offset and self.last_update:
			from_postdate = self.FixDate(self.last_update)
		else:
			if self.postdate_offset:
				from_postdate = self.OffsetDateFromToday(today, self.postdate_offset * -1)
			if self.pos_releasedate_offset:
                                to_releasedate = self.OffsetDateFromToday(today, self.pos_releasedate_offset)
			if self.neg_releasedate_offset:
                                from_releasedate = self.OffsetDateFromToday(today, self.neg_releasedate_offset)

		posted = []

		if from_postdate:
			print "Selecting trailers that were posted between ", datetime.date.isoformat(from_postdate), " and ",datetime.date.isoformat(today), "."
			for m in movies:
				pd = self.FixDate(m.info.postdate)
				if pd >= from_postdate:
					posted.append(m)
		else:
			posted = movies

		if to_releasedate or from_releasedate:

                        if to_releasedate and from_releasedate:
                                print "Selecting trailers being released between ", datetime.date.isoformat(from_releasedate)," and ", datetime.date.isoformat(to_releasedate)
                        elif to_releasedate:
                                print "Selecting trailers being released between ", datetime.date.isoformat(today)," and ", datetime.date.isoformat(to_releasedate)
			else:
                                print "Selecting trailers being released between ", datetime.date.isoformat(from_releasedate)," and ", datetime.date.isoformat(today)
			
			selections = []

			for m in posted:
				if m.info.releasedate:
					rd = self.FixDate(m.info.releasedate)
					if to_releasedate and from_releasedate:
                                                if  from_releasedate <= rd <= to_releasedate:
                                                        selections.append(m)
                                        elif to_releasedate:
                                                if today <= rd <= to_releasedate:
                                                        selections.append(m)
                                        else:
                                                if today >= rd >= from_releasedate:
                                                        selections.append(m)

				else:
					print "> ", self.HandleUnicode(m.info.title)," has no release date."
			self.movies = selections
		else:
                        self.movies = posted

		print "Total selected: ", len(self.movies)

		self.Write_LastUpdate()
			

def Usage():
  print __doc__

  
def main():

	options_string	= "hq:p:s:r:d:w:ui"
	options_list 	= ["help", "quality=", "postdate=", "days_since_release=", "days_to_release=", "dirpath=", "write_info=", "use_poster", "show_info"]

	try:
		opts, args = getopt.gnu_getopt(sys.argv[1:], options_string, options_list)
	except getopt.GetoptError, err:
		print str(err)
		Usage()
		sys.exit(2)

	quality		        = DEF_QUALITY
	postdate	        = None
	days_since_releasedate	= None
	days_to_releasedate	= None
	use_poster	        = False
	write_info	        = False
	minimal                 = False
	show_info	        = False
	dirpath		        = None
	parsed		        = False

	for o, a in opts:
		usage_exit = False
		if o in ("-h", "--help"):
			Usage()
			sys.exit()
		elif o in ("-q", "--quality"):
			if a.isalpha() or a not in Trailers.res_lookup.keys():
				usage_exit = True
			else:
				quality = a
		elif o in ("-p", "--postdate"):
			if a.isdigit():
				postdate = int(a)
			else:
				usage_exit = True
		elif o in ("-s", "--days_since_release"):
			if a.isdigit():
				days_since_releasedate = -int(a)
			else:
				usage_exit = True
		elif o in ("-r", "--days_to_release"):
			if a.isdigit():
				days_to_releasedate = int(a)
			else:
				usage_exit = True
		elif o in ("-d", "--dirpath"):
			dirpath = a
			try:
				os.path.exists(dirpath)
			except:
				print "ERROR: ", dirpath, " does not exist."
				usage_exit = True
		elif o in ("-u", "--use_poster"):
			use_poster = True
		elif o in ("-w", "--write_info"):
			if a.isdigit():
                                write_info = True
				minimal = int(a)
			else:
				usage_exit = True
		elif o in ("-i", "--show_info"):
			show_info = True

		if usage_exit:
			Usage()
			sys.exit(2)
			usage_exit = False
          
	if not dirpath:
		dirpath = os.getcwd()

	m = Movies(postdate, days_since_releasedate, days_to_releasedate, quality, use_poster, dirpath)
	m.GetMovies()
	m.Write_m3u()
	m.Write_ASX()
	if show_info:
		m.ShowInfo()
	if write_info:
		m.Write_TrailerInfo(minimal)


if __name__ == '__main__':
    main()

